﻿using System.ComponentModel;
using System.Windows;
using System.Windows.Markup;
using System;
using Microsoft.FSharp.Math;
using Microsoft.FSharp.Math.Experimental;

namespace Geometry
{
    public sealed class CircumCenter : GObjectBase, IGPoint
    {
        public CircumCenter()
        {
            UpdateBoundingBox();
        }

        public CircumCenter(IGPoint _point1, IGPoint _point2, IGPoint _point3)
        {
            this.Point1 = _point1;
            this.Point2 = _point2;
            this.Point3 = _point3;
            UpdateBoundingBox();
        }

        protected override void ReleaseDependencies()
        {
            this.Point1 = null;
            this.Point2 = null;
            this.Point3 = null;
        }

        protected override void UpdateBoundingBox()
        {
            base.BoundingBox = new Rect(Position, Position);
        }

        private IGPoint m_Point1;
        public IGPoint Point1
        {
            get
            {
                return m_Point1;
            }
            set
            {
                if (m_Point1 == value) return;

                if (m_Point1 != null) m_Point1.PropertyChanged -= _related_PropertyChanged;
                m_Point1 = value;
                if (m_Point1 != null) m_Point1.PropertyChanged += _related_PropertyChanged;

                RaisePropertyChanged("Point1");
                UpdateCalculations();
            }
        }


        private IGPoint m_Point2;
        public IGPoint Point2
        {
            get
            {
                return m_Point2;
            }
            set
            {
                if (m_Point2 == value) return;

                if (m_Point2 != null) m_Point2.PropertyChanged -= _related_PropertyChanged;
                m_Point2 = value;
                if (m_Point2 != null) m_Point2.PropertyChanged += _related_PropertyChanged;

                RaisePropertyChanged("Point2");
                UpdateCalculations();
            }
        }


        private IGPoint m_Point3;
        public IGPoint Point3
        {
            get
            {
                return m_Point3;
            }
            set
            {
                if (m_Point3 == value) return;

                if (m_Point3 != null) m_Point3.PropertyChanged -= _related_PropertyChanged;
                m_Point3 = value;
                if (m_Point3 != null) m_Point3.PropertyChanged += _related_PropertyChanged;

                RaisePropertyChanged("Point3");
                UpdateCalculations();
            }
        }


        private void _related_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName != "Position") return;

            UpdateCalculations();
        }

        private void UpdateCalculations()
        {
            if (m_Point1.IsNull()) return;
            if (m_Point2.IsNull()) return;
            if (m_Point3.IsNull()) return;
            try
            {
                UncheckedCalculation();
            }
            catch (Exception exc)
            {
                // TODO: report this error
            }
        }

        private void UncheckedCalculation()
        {
            var x1 = Point1.PosX;
            var x2 = Point2.PosX;
            var x3 = Point3.PosX;
            var y1 = Point1.PosY;
            var y2 = Point2.PosY;
            var y3 = Point3.PosY;
            var a = MatrixModule.of_array2D(
                new double[3, 3] {
                    {x1, y1, 1},
                    {x2, y2, 1},
                    {x3, y3, 1} 
                });
            var da = LinearAlgebra.Determinant(a);

            var bx = MatrixModule.of_array2D(
                new double[3, 3] {
                    {x1*x1 + y1*y1, y1, 1},
                    {x2*x2 + y2*y2, y2, 1},
                    {x3*x3 + y3*y3, y3, 1} 
                });
            var dbx = - LinearAlgebra.Determinant(bx);

            var by = MatrixModule.of_array2D(
                new double[3, 3] {
                    {x1*x1 + y1*y1, x1, 1},
                    {x2*x2 + y2*y2, x2, 1},
                    {x3*x3 + y3*y3, x3, 1} 
                });
            var dby = LinearAlgebra.Determinant(by);


            //var c = MatrixModule.of_array2D(
            //    new double[3, 3] {
            //        {x1*x1 + y1*y1, x1, y1},
            //        {x2*x2 + y2*y2, x2, y2},
            //        {x3*x3 + y3*y3, x3, y3} 
            //    });
            //var dc = - LinearAlgebra.Determinant(c);

            var x = (-dbx) / (2 * da);
            var y = (-dby) / (2 * da);

            this.Position = new Point(x, y);
        }

        #region IGPoint Members

        private Point m_Centre;
        public Point Position
        {
            get { return m_Centre; }
            private set
            {
                if (value.Equals(m_Centre)) return;
                m_Centre = value;
                UpdateBoundingBox();
                RaisePropertyChanged("Position");
                RaisePropertyChanged("PosX");
                RaisePropertyChanged("PosY");
            }
        }

        public double PosX
        {
            get { return m_Centre.X; }
        }

        public double PosY
        {
            get { return m_Centre.Y; }
        }

        #endregion
    }
}
